/**
 * 
 */
package tau.adx.simulator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

import lombok.Data;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import org.apache.log4j.Logger;

import tau.adx.common.bids.bundles.AdxBidBundle;
import tau.adx.common.devices.Device;
import tau.adx.common.devices.generators.SimpleDeviceGenerator;
import tau.adx.common.messages.users.UserState;
import tau.adx.common.publishers.AdxPublisher;
import tau.adx.common.users.AdxUser;

/**
 * @author greenwald
 * 
 */
public class SingleDay {

	/**
	 * @param users
	 *            {@link Collection} of {@link AdxUser users} to generate an
	 *            {@link AdxBidBundle} for.
	 * @throws InterruptedException
	 */
	public static void simulateUsersActivity(Collection<AdxUser> users)
			throws InterruptedException {
		logger.debug("Simulating user acticity for " + users.size() + " users");
		long before = System.currentTimeMillis();
		ExecutorService executor = Executors.newCachedThreadPool();
		List<Callable<Object>> todo = new ArrayList<Callable<Object>>(
				users.size());
		for (AdxUser adxUser : users) {
			UserRunner attempt = new UserRunner(adxUser);
			todo.add(Executors.callable(attempt));
		}
		executor.invokeAll(todo);
		executor.shutdown();
		long after = System.currentTimeMillis();
		logger.info("User activity simulation time: " + (after - before)
				+ " milliseconds");
	}

	/**
	 * This class is used to distribute the load necessary to simulate
	 * {@link AdxUser active users}. Usually used with a
	 * {@link ThreadPoolExecutor}.
	 * 
	 * @author greenwald
	 * 
	 */
	@Data
	@RequiredArgsConstructor
	private static class UserRunner implements Runnable {
		/**
		 * {@link AdxUser} to create an entry in the map for.
		 */
		@NonNull
		private AdxUser user;

		/**
		 * @see java.lang.Runnable#run()
		 */
		@Override
		public void run() {
			user.setState(UserState.ACTIVE);
			try {
				for (AdxPublisher publisher : AdX.getInstance().getPublishers()) {
					Device device = SimpleDeviceGenerator.randomDevice();
					double willVisit = publisher.willVisit(user, device);
					if (Math.random() < willVisit) {
						publisher.handleUserVisit(user, device);
						// Upon visiting web site w, a user may continue
						// visiting web sites that day with probability
						// PContinue(w).
						if (Math.random() > user.getPContinue()) {
							break;
						}
					}
				}
			} catch (Exception e) {
				logger.error("An error occured while simulating activity for: "
						+ user, e);
			}
			user.setState(UserState.IDLE);
		}

		/**
		 * {@link Logger} instance.
		 */
		private Logger logger = Logger.getLogger(SingleDay.class);
	}

	/**
	 * {@link Logger} instance.
	 */
	private static Logger logger = Logger.getLogger(SingleDay.class);
}
